package com.marisfrolg.scanner.util;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Set;
import java.util.UUID;

public class BlueToothService {
	private BluetoothAdapter adapter;
	private Context context;
	private int mState;
	private int scanState = 1;
	private Boolean D = Boolean.valueOf(true);
	private String TAG = "BlueToothService";
	private AcceptThread mAcceptThread;
	private ConnectThread mConnectThread;
	private ConnectedThread mConnectedThread;
	public static final int STATE_NONE = 0;
	public static final int STATE_LISTEN = 1;
	public static final int STATE_CONNECTING = 2;
	public static final int STATE_CONNECTED = 3;
	public static final int LOSE_CONNECT = 4;
	public static final int FAILED_CONNECT = 5;
	public static final int SUCCESS_CONNECT = 6;
	public static final int MESSAGE_STATE_CHANGE = 1;
	public static final int MESSAGE_READ = 2;
	public static final int MESSAGE_WRITE = 3;
	public static final int STATE_SCANING = 0;
	public static final int STATE_SCAN_STOP = 1;
	private static final int WRITE_READ = 2;
	private static final int WRITE_WAIT = 3;
	private static int writeState = 2;
	public static int times = 0;
	private static int PrinterType = 0;
	private static int PrinterTypeNow = 0;

	private void SetWriteState(int state) {
		synchronized (this) {
			writeState = state;
		}
	}

	private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	private static final String NAME = "BTPrinter";
	private Handler mHandler;

	public BlueToothService(Context context, Handler handler) {
		this.context = context;
		this.mHandler = handler;
		this.mState = 0;
		this.adapter = BluetoothAdapter.getDefaultAdapter();
	}

	public boolean HasDevice() {
		if (this.adapter != null) {
			return true;
		}
		return false;
	}

	public boolean IsOpen() {
		synchronized (this) {
			if (this.adapter.isEnabled()) {
				return true;
			}
			return false;
		}
	}

	public void OpenDevice() {
		Intent intent = new Intent("android.bluetooth.adapter.action.REQUEST_ENABLE");
		this.context.startActivity(intent);
	}

	public void CloseDevice() {
		this.adapter.disable();
	}

	public Set<BluetoothDevice> GetBondedDevice() {
		Set<BluetoothDevice> devices = this.adapter.getBondedDevices();
		return devices;
	}

	public void ScanDevice() {
		IntentFilter filter = new IntentFilter(
				"android.bluetooth.device.action.FOUND");
		this.context.registerReceiver(this.mReceiver, filter);

		filter = new IntentFilter("android.bluetooth.adapter.action.DISCOVERY_FINISHED");
		this.context.registerReceiver(this.mReceiver, filter);
		if (this.adapter.isDiscovering()) {
			this.adapter.cancelDiscovery();
		}
		SetScanState(0);

		this.adapter.startDiscovery();
	}

	public void StopScan() {
		this.context.unregisterReceiver(this.mReceiver);
		this.adapter.cancelDiscovery();
		SetScanState(1);
	}

	public OnReceiveDataHandleEvent OnReceive = null;

	public OnReceiveDataHandleEvent getOnReceive() {
		return this.OnReceive;
	}

	public void setOnReceive(OnReceiveDataHandleEvent onReceive) {
		this.OnReceive = onReceive;
	}

	private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if ("android.bluetooth.device.action.FOUND".equals(action)) {
				BluetoothDevice device = (BluetoothDevice) intent
						.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
				if (device.getBondState() != 12) {
					BlueToothService.this.SetScanState(0);
					BlueToothService.this.OnReceive.OnReceive(device);
				}
			} else if ("android.bluetooth.adapter.action.DISCOVERY_FINISHED"
					.equals(action)) {
				BlueToothService.this.SetScanState(1);

				BlueToothService.this.OnReceive.OnReceive(null);
			}
		}

		private void OnFinished() {
		}
	};
	public static String EXTRA_DEVICE_ADDRESS = "device_address";

	public void ConnectToDevice(String address) {
		if (BluetoothAdapter.checkBluetoothAddress(address)) {
			BluetoothDevice device = this.adapter.getRemoteDevice(address);
			PrinterType = 0;
			PrinterTypeNow = 0;
			connect(device);
		}
	}

	public void write(byte[] out) {
		ConnectedThread r;
		synchronized (this) {
			if (this.mState != 3) {
				return;
			}
			r = this.mConnectedThread;
		}
		r.write(out);
	}

	public synchronized void start() {
		if (this.D.booleanValue()) {
			Log.d(this.TAG, "start");
		}
		if (this.mConnectThread != null) {
			this.mConnectThread.cancel();
			this.mConnectThread = null;
		}
		if (this.mConnectedThread != null) {
			this.mConnectedThread.cancel();
			this.mConnectedThread = null;
		}
		if (this.mAcceptThread == null) {
			this.mAcceptThread = new AcceptThread();
			this.mAcceptThread.start();
		}
		setState(1);
	}

	private synchronized void setState(int state) {
		this.mState = state;
	}

	public synchronized int getState() {
		return this.mState;
	}

	private synchronized void SetScanState(int state) {
		this.scanState = state;
	}

	public synchronized int GetScanState() {
		return this.scanState;
	}

	public synchronized void connect(BluetoothDevice device) {
		if ((this.mState == 2) && (this.mConnectThread != null)) {
			this.mConnectThread.cancel();
			this.mConnectThread = null;
		}
		if (this.mConnectedThread != null) {
			this.mConnectedThread.cancel();
			this.mConnectedThread = null;
		}
		try {
			Thread.sleep(1000L);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.mConnectThread = new ConnectThread(device);
		this.mConnectThread.start();
		setState(2);
	}

	public synchronized void DisConnected() {
		if (this.mConnectThread != null) {
			this.mConnectThread.cancel();
			this.mConnectThread = null;
		}
		if (this.mConnectedThread != null) {
			this.mConnectedThread.cancel();
			this.mConnectedThread = null;
		}
		if (this.mAcceptThread != null) {
			this.mAcceptThread.cancel();
			this.mAcceptThread = null;
		}
		setState(0);
	}

	public synchronized void connected(BluetoothSocket socket,
			BluetoothDevice device) {
		if (this.mConnectThread != null) {
			this.mConnectThread.cancel();
			this.mConnectThread = null;
		}
		if (this.mConnectedThread != null) {
			this.mConnectedThread.cancel();
			this.mConnectedThread = null;
		}
		if (this.mAcceptThread != null) {
			this.mAcceptThread.cancel();
			this.mAcceptThread = null;
		}
		this.mConnectedThread = new ConnectedThread(socket);
		this.mConnectedThread.start();

		setState(3);
	}

	public synchronized void stop() {
		if (this.D.booleanValue()) {
			Log.d(this.TAG, "stop");
		}
		setState(0);
		if (this.mConnectThread != null) {
			this.mConnectThread.cancel();
			this.mConnectThread = null;
		}
		if (this.mConnectedThread != null) {
			this.mConnectedThread.cancel();
			this.mConnectedThread = null;
		}
		if (this.mAcceptThread != null) {
			this.mAcceptThread.cancel();
			this.mAcceptThread = null;
		}
	}

	private void connectionSuccess() {
		setState(3);
		SetPrinterInf();
		this.mHandler.obtainMessage(1, 6, -1).sendToTarget();
	}

	private void SetPrinterInf() {
		new Thread() {
			public void run() {
				try {
					Thread.sleep(500L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				BlueToothService.PrinterTypeNow = BlueToothService.PrinterType;
			}
		}.start();
	}

	private void connectionFailed() {
		setState(1);
		this.mHandler.obtainMessage(1, 5, -1).sendToTarget();
	}

	private void connectionLost() {
		setState(1);
		this.mHandler.obtainMessage(1, 4, -1).sendToTarget();
	}

	private class AcceptThread extends Thread {
		private final BluetoothServerSocket mmServerSocket;

		public AcceptThread() {
			BluetoothServerSocket tmp = null;
			try {
				tmp = BlueToothService.this.adapter
						.listenUsingRfcommWithServiceRecord("BTPrinter",
								BlueToothService.MY_UUID);
			} catch (IOException e) {
				Log.e(BlueToothService.this.TAG, "listen() failed", e);
			}
			this.mmServerSocket = tmp;
		}

		public void run() {
			if (BlueToothService.this.D.booleanValue()) {
				Log.d(BlueToothService.this.TAG, "BEGIN mAcceptThread" + this);
			}
			setName("AcceptThread");
			BluetoothSocket socket = null;
			while (BlueToothService.this.mState != 3) {
				try {
					socket = this.mmServerSocket.accept();
				} catch (IOException e) {
					break;
				}
				if (socket != null) {
					synchronized (BlueToothService.this) {
						switch (BlueToothService.this.mState) {
						case 1:
						case 2:
							BlueToothService.this.connected(socket,
									socket.getRemoteDevice());
							break;
						case 0:
						case 3:
							try {
								socket.close();
							} catch (IOException localIOException1) {
							}
						}
					}
				}
			}
		}

		public void cancel() {
			if (BlueToothService.this.D.booleanValue()) {
				Log.d(BlueToothService.this.TAG, "cancel " + this);
			}
			try {
				this.mmServerSocket.close();
			} catch (IOException e) {
				Log.e(BlueToothService.this.TAG, "close() of server failed", e);
			}
		}
	}

	private class ConnectThread extends Thread {
		private final BluetoothSocket mmSocket;
		private final BluetoothDevice mmDevice;

		public ConnectThread(BluetoothDevice device) {
			this.mmDevice = device;
			BluetoothSocket tmp = null;
			try {
				tmp = device
						.createRfcommSocketToServiceRecord(BlueToothService.MY_UUID);
			} catch (IOException e) {
				Log.e(BlueToothService.this.TAG, "create() failed", e);
			}
			this.mmSocket = tmp;
		}

		public void run() {
			Log.i(BlueToothService.this.TAG, "BEGIN mConnectThread");
			setName("ConnectThread");

			BlueToothService.this.adapter.cancelDiscovery();
			BlueToothService.this.SetScanState(1);
			try {
				this.mmSocket.connect();
				BlueToothService.this.connectionSuccess();
			} catch (IOException e) {
				BlueToothService.this.connectionFailed();
				try {
					this.mmSocket.close();
				} catch (IOException e2) {
					Log.e(BlueToothService.this.TAG,
							"unable to close() socket during connection failure",
							e2);
				}
				BlueToothService.this.start();
				return;
			}
			synchronized (BlueToothService.this) {
				BlueToothService.this.mConnectThread = null;
			}
			BlueToothService.this.connected(this.mmSocket, this.mmDevice);
		}

		public void cancel() {
			try {
				this.mmSocket.close();
			} catch (IOException e) {
				Log.e(BlueToothService.this.TAG,
						"close() of connect socket failed", e);
			}
		}
	}

	public void SetPrinterType(int type) {
		PrinterType = type;
	}

	private class ConnectedThread extends Thread {
		private final BluetoothSocket mmSocket;
		private final InputStream mmInStream;
		private final OutputStream mmOutStream;
		private boolean isCancle = false;

		public ConnectedThread(BluetoothSocket socket) {
			Log.d(BlueToothService.this.TAG, "create ConnectedThread");
			this.mmSocket = socket;
			InputStream tmpIn = null;
			OutputStream tmpOut = null;
			this.isCancle = false;
			try {
				tmpIn = socket.getInputStream();
				tmpOut = socket.getOutputStream();
			} catch (IOException e) {
				Log.e(BlueToothService.this.TAG, "temp sockets not created", e);
			}
			this.mmInStream = tmpIn;
			this.mmOutStream = tmpOut;
		}

		public void run() {
			Log.i(BlueToothService.this.TAG, "BEGIN mConnectedThread");
			while (!this.isCancle) {
				try {
					byte[] buffer = new byte[1];

					int bytes = this.mmInStream.read(buffer);
					if (bytes > 0) {
						BlueToothService.this.SetPrinterType(2);
						if (buffer[0] != 17) {
							BlueToothService.this.SetWriteState(3);
						} else {
							BlueToothService.this.SetWriteState(2);
						}
					} else {
						Log.e(BlueToothService.this.TAG, "disconnected1");
						BlueToothService.this.connectionLost();
						this.isCancle = true;
					}
				} catch (IOException e) {
					Log.e(BlueToothService.this.TAG, "disconnected2", e);
					BlueToothService.this.connectionLost();
					this.isCancle = true;
				}
			}
		}

		public void write(byte[] buffer) {
			try {
				this.mmOutStream.write(buffer);
				Log.i("BTPWRITE", new String(buffer, "GBK"));

				BlueToothService.this.mHandler.obtainMessage(3, -1, -1, buffer)
						.sendToTarget();
			} catch (IOException localIOException) {
			}
		}

		public void cancel() {
			try {
				this.isCancle = true;
				this.mmSocket.close();
				Log.d(BlueToothService.this.TAG, "562cancel suc");
				BlueToothService.this.setState(1);
			} catch (IOException e) {
				Log.d(BlueToothService.this.TAG, "565cancel failed");
			}
		}
	}

	public void PrintCharacters(String str) {
		byte[] send;
		try {
			send = str.getBytes("GBK");
		} catch (UnsupportedEncodingException e) {
			send = str.getBytes();
		}
		write(send);
	}

	public void SendOrder(byte[] send) {
		write(send);
	}

	public void PrintImage(Bitmap bitmapcode,byte co) {
		if (PrinterTypeNow == 2) {
			PrintImageOld(bitmapcode);
		} else {
			PrintImageNew(bitmapcode,co);
		}
	}

	public void PrintImageOld(Bitmap bitmapCode) {
		byte[] sendbuf = StartBmpToPrintCode(bitmapCode);

		int num = 0;
		int total = 1152;
		while (num != sendbuf.length) {
			if (writeState == 2) {
				byte[] sendper;
				if (sendbuf.length - num > total) {
					sendper = new byte[total];
					System.arraycopy(sendbuf, num, sendper, 0, total);
					num += total;
				} else {
					sendper = new byte[sendbuf.length - num];
					System.arraycopy(sendbuf, num, sendper, 0, sendbuf.length
							- num);
					num = sendbuf.length;
				}
				try {
					Thread.sleep(500L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				write(sendper);
			}
		}
	}

	public void PrintImageNew(Bitmap bitmapCode,byte co) {
		int w = bitmapCode.getWidth();
		int h = bitmapCode.getHeight();
		byte[] sendbuf = StartBmpToPrintCode2(bitmapCode);

		write(sendbuf);
		byte[] sendbuf2 =  new byte[4];
		sendbuf2[0] = 27;
		sendbuf2[1] = 49;
		sendbuf2[2] = co;
		sendbuf2[3] = 10;
		write(sendbuf2);
	}
	
	private byte[] StartBmpToPrintCode(Bitmap bitmap) {
		byte temp = 0;
		int j = 7;
		int start = 0;
		if (bitmap != null) {
			int mWidth = bitmap.getWidth();
			int mHeight = bitmap.getHeight();

			int[] mIntArray = new int[mWidth * mHeight];
			byte[] data = new byte[mWidth * mHeight];
			bitmap.getPixels(mIntArray, 0, mWidth, 0, 0, mWidth, mHeight);
			encodeYUV420SP(data, mIntArray, mWidth, mHeight);
			byte[] result = new byte[mWidth * mHeight / 8];
			for (int i = 0; i < mWidth * mHeight; i++) {
				temp = (byte) ((byte) (data[i] << j) + temp);
				j--;
				if (j < 0) {
					j = 7;
				}
				if (i % 8 == 7) {
					result[(start++)] = temp;
					temp = 0;
				}
			}
			if (j != 7) {
				result[(start++)] = temp;
			}
			int aHeight = 24 - mHeight % 24;
			byte[] add = new byte[aHeight * 48];
			byte[] nresult = new byte[mWidth * mHeight / 8 + aHeight * 48];
			System.arraycopy(result, 0, nresult, 0, result.length);
			System.arraycopy(add, 0, nresult, result.length, add.length);

			byte[] byteContent = new byte[(mWidth / 8 + 4) * (mHeight + aHeight)];
			byte[] bytehead = new byte[4];
			bytehead[0] = 31;
			bytehead[1] = 16;
			bytehead[2] = ((byte) (mWidth / 8));
			bytehead[3] = 0;
			for (int index=0; index < mHeight + aHeight; index++) {
				if(index==180){
					System.out.println();
				}
				System.arraycopy(bytehead, 0, byteContent, index * (48+4), 4);
				System.arraycopy(nresult, index * 48, byteContent,index * (48+4) + 4, 48);
			}	
			return byteContent;
		}
		return null;
	}

	
	private byte[] StartBmpToPrintCode2(Bitmap bitmap) {
		byte temp = 0;
		int j = 7;
		int start = 0;
		if (bitmap != null) {
			int mWidth = bitmap.getWidth();
			int mHeight = bitmap.getHeight();

			int[] mIntArray = new int[mWidth * mHeight];
			byte[] data = new byte[mWidth * mHeight];
			bitmap.getPixels(mIntArray, 0, mWidth, 0, 0, mWidth, mHeight);
			encodeYUV420SP(data, mIntArray, mWidth, mHeight);
			byte[] result = new byte[mWidth * mHeight / 8];
			for (int i = 0; i < mWidth * mHeight; i++) {
				temp = (byte) ((byte) (data[i] << j) + temp);
				j--;
				if (j < 0) {
					j = 7;
				}
				if (i % 8 == 7) {
					result[(start++)] = temp;
					temp = 0;
				}
			}
			if (j != 7) {
				result[(start++)] = temp;
			}
			int aWidth = mWidth / 8;
			int aHeight = 24 - mHeight % 24;
			byte[] add = new byte[aHeight * aWidth];
			byte[] nresult = new byte[mWidth * mHeight / 8 + aHeight * aWidth];
			System.arraycopy(result, 0, nresult, 0, result.length);
			System.arraycopy(add, 0, nresult, result.length, add.length);

			byte[] byteContent = new byte[(mWidth / 8 + 4) * (mHeight + aHeight)];
			byte[] bytehead = new byte[4];
			bytehead[0] = 31;
			bytehead[1] = 16;
			bytehead[2] = ((byte) (mWidth / 8));
			bytehead[3] = 0;
			for (int index=0; index < mHeight + aHeight; index++) {
				System.arraycopy(bytehead, 0, byteContent, index * (aWidth+4), 4);
				System.arraycopy(nresult, index * aWidth, byteContent,index * (aWidth+4) + 4, aWidth);
			}	
			return byteContent;
		}
		return null;
	}


	public void encodeYUV420SP(byte[] yuv420sp, int[] rgba, int width,
			int height) {
		int frameSize = width * height;

		int[] U = new int[frameSize];
		int[] V = new int[frameSize];
		int uvwidth = width / 2;

		int bits = 8;
		int index = 0;
		int f = 0;
		for (int j = 0; j < height; j++) {
			for (int i = 0; i < width; i++) {
				int r = (rgba[index] & 0xFF000000) >> 24;
				int g = (rgba[index] & 0xFF0000) >> 16;
				int b = (rgba[index] & 0xFF00) >> 8;

				int y = (66 * r + 129 * g + 25 * b + 128 >> 8) + 16;
				int u = (-38 * r - 74 * g + 112 * b + 128 >> 8) + 128;
				int v = (112 * r - 94 * g - 18 * b + 128 >> 8) + 128;

				byte temp = (byte) (y > 255 ? 255 : y < 0 ? 0 : y);
				yuv420sp[(index++)] = (byte) (temp > 0 ? 1 : 0);
			}
		}
		f = 0;
	}

	public static abstract interface OnReceiveDataHandleEvent {
		public abstract void OnReceive(BluetoothDevice paramBluetoothDevice);
	}
}
